/*
The MIT License
Copyright (c) 2019 Lehrstuhl Informatik 11 - RWTH Aachen University
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE

This file is part of embeddedRTPS.

Author: i11 - Embedded Software, RWTH Aachen University
*/

#ifndef RTPS_TYPES_H
#define RTPS_TYPES_H

#include "lwip/ip4_addr.h"

//#include <array>
// #include <cstdint>
// #include <cstring>
// #include <initializer_list>

#include <stdbool.h>
#include <stdint.h>
#include <string.h>

// TODO move types to where they are needed!

typedef uint16_t Ip4Port_t;
typedef uint16_t DataSize_t;
typedef int8_t ParticipantId_t; // With UDP only 120 possible

typedef uint8_t EntityKind_t;
#define  USER_DEFINED_UNKNOWN  0x00
  // No user define participant
#define  USER_DEFINED_WRITER_WITH_KEY  0x02
#define  USER_DEFINED_WRITER_WITHOUT_KEY  0x03
#define  USER_DEFINED_READER_WITHOUT_KEY  0x04
#define  USER_DEFINED_READER_WITH_KEY  0x07
#define  BUILD_IN_UNKNOWN  0xc0
#define  BUILD_IN_PARTICIPANT  0xc1
#define  BUILD_IN_WRITER_WITH_KEY  0xc2
#define  BUILD_IN_WRITER_WITHOUT_KEY  0xc3
#define  BUILD_IN_READER_WITHOUT_KEY  0xc4
#define  BUILD_IN_READER_WITH_KEY  0xc7
#define  VENDOR_SPEC_UNKNOWN  0x40
#define  VENDOR_SPEC_PARTICIPANT  0x41
#define  VENDOR_SPEC_WRITER_WITH_KEY  0x42
#define  VENDOR_SPEC_WRITER_WITHOUT_KEY  0x43
#define  VENDOR_SPEC_READER_WITHOUT_KEY  0x44
#define  VENDOR_SPEC_READER_WITH_KEY  0x47

typedef uint8_t TopicKind_t;
#define  NO_KEY  1 
#define  WITH_KEY  2 


typedef uint8_t ChangeKind_t;
#define  INVALID  0
#define  ALIVE  1
#define  NOT_ALIVE_DISPOSED  2
#define  NOT_ALIVE_UNREGISTERED  3


typedef enum ReliabilityKind_t {
  BEST_EFFORT = 1,
  RELIABLE = 2 // Specification says 3 but eprosima sends 2
}ReliabilityKind_t;

typedef enum DurabilityKind_t {
  VOLATILE = 0,
  TRANSIENT_LOCAL = 1,
  TRANSIENT = 2,
  PERSISTENT = 3
}DurabilityKind_t;

typedef struct GuidPrefix_t {
  uint8_t id[12];
}GuidPrefix_t;

//operator==
inline bool rtps_GuidPrefix_t_operator_equal (const struct GuidPrefix_t *A, const struct GuidPrefix_t *B) {
    return !memcmp(A->id, B->id, sizeof(A->id));
}

typedef struct EntityId_t {
  uint8_t entityKey[3];
  EntityKind_t entityKind;
}EntityId_t;

//operator==
inline bool rtps_EntityId_t_operator_equal (const struct EntityId_t *A,const struct EntityId_t *B) {
  return (!memcmp(A->entityKey, B->entityKey, sizeof(A->entityKey)) &&
           (A->entityKind == B->entityKind));
}
//operator!=
inline bool rtps_EntityId_t_operator_unequal (const struct EntityId_t *A,const struct EntityId_t *B) { 
  return ((A->entityKind != B->entityKind) || (A->entityKey != B->entityKey));
}

typedef struct Guid_t {
  struct GuidPrefix_t prefix;
  struct EntityId_t entityId;
}Guid_t;
//operator==
inline bool rtps_Guid_t_operator_equal (const Guid_t *A, const Guid_t *B) {
    return (!memcmp(&A->prefix, &B->prefix, sizeof(A->prefix)) 
            && !memcmp(&A->entityId, &B->entityId, sizeof(A->entityId)));
}
//hanjie:to check right or not
//sum
inline uint32_t rtps_Guid_t_operator_sum(const Guid_t *other) {
    uint32_t ret = 0;
    for (uint8_t i=0; i < (sizeof(other->prefix.id)/sizeof(uint8_t)); i++) {
      ret += other->prefix.id[i];
    }
    for (uint8_t i=0; i < (sizeof(other->entityId.entityKey)/sizeof(uint8_t)); i++) {
      ret += other->entityId.entityKey[i];
    }
    return ret;
}

typedef struct Time_t {
  int32_t seconds;   // time in seconds
  uint32_t fraction; // time in sec/2^32 (?)
}Time_t;
// Described as long but there wasn't any definition. Other than 32 bit does not
// conform the default values
//create
static inline Time_t *rtps_Time_t_operator_create(int32_t s, uint32_t ns) {
  static double factor = (1UL << 32) / 1000000000.;
  uint32_t fraction = (uint32_t)(ns * factor);

  struct Time_t *pTime = NULL;
  pTime = (struct Time_t *)malloc(sizeof(struct Time_t));
  if (!pTime)
    return NULL;
  pTime->seconds = s;
  pTime->fraction = fraction;
  return pTime;
}

typedef struct VendorId_t {
  uint8_t vendorId[2];
}VendorId_t;

typedef struct SequenceNumber_t {
  int32_t high;
  uint32_t low;
}SequenceNumber_t;
//operator==
inline bool rtps_SequenceNumber_t_operator_equal(const SequenceNumber_t *A, const SequenceNumber_t *B){
  return A->high == B->high && A->low == B->low;
}
//operator!=
inline bool rtps_SequenceNumber_t_operator_unequal(const SequenceNumber_t *A, const SequenceNumber_t *B){
  return ((A->high != B->high) || (A->low != B->low));
}
//operator<
inline bool rtps_SequenceNumber_t_operator_less(const SequenceNumber_t *A, const SequenceNumber_t *B){
  return A->high < B->high || (A->high == B->high && A->low < B->low);
}
//operator<=
inline bool rtps_SequenceNumber_t_operator_lessequal(const SequenceNumber_t *A, const SequenceNumber_t *B){
  return (rtps_SequenceNumber_t_operator_less(A, B) 
          || rtps_SequenceNumber_t_operator_equal(A, B));
}
//operator++
inline struct SequenceNumber_t *rtps_SequenceNumber_t_operator_increase(struct SequenceNumber_t *A){
    ++A->low;
    if (A->low == 0) {
      ++A->high;
    }
    return A;
}
//operator+a
inline struct SequenceNumber_t *rtps_SequenceNumber_t_operator_increase_a(struct SequenceNumber_t *A){
    //hanjie:TODO
    //struct SequenceNumber_t tmp;
    //SequenceNumber_t tmp(*this);
    //++*A;
    return A;
}

#define SNS_NUM_BITS  32
typedef struct SequenceNumberSet {
  struct SequenceNumber_t base;
  // Cannot be static because of packed
  // uint32_t numBits = SNS_NUM_BITS;
  uint32_t numBits;
  uint32_t bitMap[8];
}SequenceNumberSet;

inline void rtps_SequenceNumberSet_Construct(SequenceNumberSet *pSet, const SequenceNumber_t *firstMissing){
  if (firstMissing) {
    memcpy(&pSet->base, firstMissing, sizeof(SequenceNumber_t));
  } else {
    pSet->base.high = 0;
    pSet->base.low = 0;
  }
  pSet->numBits = SNS_NUM_BITS;
}

  // We only need 1 byte because atm we don't store packets.
inline bool rtps_SequenceNumberSet_isSet(struct SequenceNumberSet *pData, uint32_t bit){
  if (bit >= SNS_NUM_BITS) {
    return true;
  }
  const uint8_t bucket = (uint8_t)(bit / 32);
  const uint8_t pos = (uint8_t)(bit % 32);
  return (pData->bitMap[bucket] & (1 << (31 - pos))) != 0;
}

typedef struct FragmentNumber_t {
  uint32_t value;
}FragmentNumber_t;

typedef struct Count_t {
  int32_t value;
}Count_t;

typedef struct ProtocolVersion_t {
  uint8_t major;
  uint8_t minor;
}ProtocolVersion_t;

typedef Time_t Duration_t; // TODO

typedef enum ChangeForReaderStatusKind {
  UNSENT,
  UNACKNOWLEDGED,
  REQURESTED,
  ACKNOWLEDGED,
  UNDERWAY
}ChangeForReaderStatusKind;

typedef enum ChangeFromWriterStatusKind {
   LOST, MISSING, RECEIVED, UNKNOWN 
}ChangeFromWriterStatusKind;

typedef struct InstanceHandle_t { // TODO
  uint64_t value;
}InstanceHandle_t;
#if 0
struct ParticipantMessageData { // TODO
};
#endif

/* Default Values */
extern const EntityId_t ENTITYID_UNKNOWN ;
extern const EntityId_t ENTITYID_BUILD_IN_PARTICIPAN;
extern const EntityId_t ENTITYID_SEDP_BUILTIN_TOPIC_WRITER;
extern const EntityId_t ENTITYID_SEDP_BUILTIN_TOPIC_READER;
extern const EntityId_t ENTITYID_SEDP_BUILTIN_PUBLICATIONS_WRITER;
extern const EntityId_t ENTITYID_SEDP_BUILTIN_PUBLICATIONS_READER;
extern const EntityId_t ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_WRITER;
extern const EntityId_t ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_READER;
extern const EntityId_t ENTITYID_SPDP_BUILTIN_PARTICIPANT_WRITER;
extern const EntityId_t ENTITYID_SPDP_BUILTIN_PARTICIPANT_READER;
extern const EntityId_t ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER;
extern const EntityId_t ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER;
extern const GuidPrefix_t GUIDPREFIX_UNKNOWN;
extern const Guid_t GUID_UNKNOWN;
extern const ParticipantId_t PARTICIPANT_ID_INVALID;
extern const ProtocolVersion_t PROTOCOLVERSION_1_0;
extern const ProtocolVersion_t PROTOCOLVERSION_1_1;
extern const ProtocolVersion_t PROTOCOLVERSION_2_0;
extern const ProtocolVersion_t PROTOCOLVERSION_2_1;
extern const ProtocolVersion_t PROTOCOLVERSION_2_2;
extern const ProtocolVersion_t PROTOCOLVERSION;
extern const SequenceNumber_t SEQUENCENUMBER_UNKNOWN;
extern const Time_t TIME_ZERO;
extern const Time_t TIME_INVALID;
extern const Time_t TIME_INFINITE;
extern const VendorId_t VENDOR_UNKNOWN;

#endif // RTPS_TYPES_H
